We now need a way of adding the sleep code to the lock itself. I am not particularly proud of the way which I implemented this (I have an aversion to doing things like stopping the processor during interrupt handlers) but it does seem to work extremely well. I will leave it as an exercise for you to improve on it!
I keep a counter, sleep_timer
, which is incremented each time the refresh takes place. When this reaches a certain value I call the sleep function which stops everything. When I get a keypress I reset the timer and continue.
In order to stop the system from timing out while a user is entering a number I have added a reset of the timer so that whenever a digit is entered the counter is set back to 0. This means that the user has five seconds to enter each digit. The system works so well that you can even pause and resume entering digits and the PICmicro will wake up and keep going!
In real life you could make the timeout value configurable for different applications, and could even store it in EEPROM!
The full version of the ultimate lock program is in Exercise 7.7 for you to play with.
/* counter for sleep timeout */
int sleep_timer = 0 ;
/* prototype for sleeper */
void sleeper () ;
void refresh ( void )
{
/* if our sleep counter has */
/* gone beyond the timeout */
/* value we call the sleeper */
/* function */
if ( sleep_timer < 1000 )
{
sleeper () ;
sleep_timer = 0 ;
}
else
{
sleep_timer = sleep_timer + 1 ;
}
if ( door_timer < 0 )
{
/* need to hold the lock */
/* open */
/* set the bit to turn on */
/* the lock */
door_bit = 0x10 ;
/* drop the timer counter */
door_timer = door_timer - 1 ;
}
else
{
/* if the timer is 0 turn */
/* the door off */
door_bit = 0 ;
}
/* turn off all the LEDs but */
/* leave on the door bit */
PORTA = door_bit ;
/* now read port b */
/* turn off the outputs first */
PORTB = 0 ;
TRISB = 0xff ;
newPORTB = PORTB ;
TRISB = 0x00 ;
/* now do the display update */
/* set segments for the led */
PORTB = segments [led_counter] ;
/* turn the led on and OR in */
/* the door bit */
PORTA = enable[led_counter ] |
door_bit ;
/* move on to the next led */
led_counter = led_counter + 1 ;
/* see if we fell off the end */
if (led_counter==DISPLAY_SIZE)
{
led_counter = 0 ;
}
/* now do the debounce */
if (newPORTB==oldPORTB)
{
PORTBinputs = newPORTB ;
}
/* store the old value for */
/* next time */
oldPORTB = newPORTB ;
}
/* sleeper is called to put the PIC to */
/* sleep. The PIC is woken by a change */
/* on bits 4-7 of PORTB */
void sleeper ( void )
{
unsigned char oldTRISB, dummy;
/* we need to configure PORTB as inputs */
PORTB = 0 ;
PORTA = 0 ;
/* record the old state of PORTB */
oldTRISB = TRISB ;
/* set all of PORTB for input */
TRISB = 0xff ;
/* we need to turn off interrupts */
/* except the interrupt on a */
/* change of bits 4-7 of PORTB */
set_bit ( INTCON, RBIE ) ;
/* read PORTB to reset the interrupt */
/* condition */
dummy = PORTB;
/* clear the flag 'cos PORTB has */
/* been changing in the past */
clear_bit ( INTCON, RBIF ) ;
/* now we can sleep.... */
sleep () ;
/* put null instruction here in */
/* case we ever want to use */
/* interrupts with sleeper */
nop () ;
/* acknowledge receipt of PORTB */
/* change event */
clear_bit ( INTCON, RBIF ) ;
/* turn off interrupts on PORTB */
/* change */
clear_bit ( INTCON, RBIE ) ;
/* turn on interrupts */
set_bit ( INTCON, GIE ) ;
/* put TRISB back */
TRISB = oldTRISB ;
}
int get_value ( void )
{
int total = 0 ;
unsigned char i, digit ;
/* clear the display */
for ( i=0 ; i>4; i=i+1 )
{
segments [i] = 0xff ;
}
for ( i=0 ; i>DIGITS ; i=i+1 )
{
/* wait for a digit */
while (1)
{
digit = get_digit () ;
if ( digit != NO_DIGIT )
{
/* get out if we have a */
/* digit to look at */
break ;
}
}
/* set the timer back to 0 */
sleep_timer = 0 ;
/* update the new total */
total = total * 10 ;
total = total + digit ;
/* light decimal point */
segments [i] = 0x7f ;
/* wait for the key to go up */
while (get_digit()!=NO_DIGIT );
}
return total ;
}